home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue33 / intrposr / EnhCtrls.pas next >
Encoding:
Pascal/Delphi Source File  |  1998-03-04  |  14.2 KB  |  546 lines

  1. unit EnhCtrls;
  2. // set of Interposer Components enhancing some of the
  3. // standard Delphi VCL components with often requested additions
  4. //
  5. //
  6. // ⌐Stephen Posey -- slposey@concentric.net
  7. // Written for The Delphi Magazine
  8. //
  9. //////////////////////////////////////////////////////////////////
  10. // Usage: simply add this Unit to the Form's uses clause AFTER
  11. // the unit(s) that declare the original components; then
  12. // just use the provided new methods and properties in your code
  13. // as if they were part of the original class!
  14. //
  15. //////////////////////////////////////////////////////////////////
  16. // References:
  17. // 1) Rubenking, N. (1996).
  18. //    Delphi Programming Problem Solver.
  19. //    Foster City, California, USA: IDG Books.
  20. //    ISBN: 1-56884-795-5
  21. //
  22. // 2) Miano, J.; Cabanski, T.; & Howe, H. (1997).
  23. //    The Waite Group's Borland C++ Builder How-To.
  24. //    Corte Madera, California, USA: Waite Group Press.
  25. //    ISBN: 1-57169-109-X.
  26. //
  27. // 3) Frerking, G.; Wallace, N.; & Niddery, W. (1995).
  28. //    The Waite Group's Borland Delphi How-To.
  29. //    Corte Madera, California, USA: Waite Group Press.
  30. //    ISBN: 1-57169-019-0.
  31. //
  32. // Interposed TPanel:
  33. // * The code for exposing the Canvas property is adapted
  34. //   from widely available for creating a new TPanel descendant
  35. // * The code for adding the "Hi There" method was my invention
  36. //
  37. // Interposed TBitBtn:
  38. // * The code for the font color change when the mouse is over.
  39. //   the button I've had since D1 days, don't recall where I got it.
  40. // * The sound code I came up with for this project.
  41. //
  42. // Interposed TListBox:
  43. // * Both the code for adding the Horizontal scrollbar is adapted
  44. //   from code in reference 1)
  45. // * The sound code I came up with for this project.
  46. //
  47. // Interposed TEdit:
  48. // * The code for Left and center justification is adapted from code
  49. //   in both reference 1) and 2); plus some pretty deep VCL exploration
  50. //   by your author (esp. the call to RecreateWnd
  51. // * The code for character filtering is adapted from code in
  52. //   reference 3)
  53. //
  54. // Interposed TMemo:
  55. // *  Both the code for caret position and for
  56. //    single step Undo is adapted from code in
  57. //    reference 1)
  58.  
  59. (*****) interface (****************************************)
  60. uses
  61.   Windows, Messages, SysUtils, Classes, Controls,
  62.   Graphics, StdCtrls, ExtCtrls, Buttons ;
  63.  
  64. type
  65.  
  66.   TPanel = class(ExtCtrls.TPanel)
  67.   // Interposed TPanel:
  68.   // Exposes the inherited Canvas property
  69.   // Shows example of ADDING an entirely NEW method to a component
  70.   public
  71.     procedure HiThere ;
  72.   published
  73.     property Canvas ;
  74.   end ;
  75.  
  76.   TBitBtn = class( Buttons.TBitBtn )
  77.   // Interposed TBitBtn:
  78.   // Adds optional font color change when the mouse is over the button
  79.   // Adds optional sound when button pressed
  80.   private
  81.     FEnterChange, FPlaySound : boolean ;
  82.     FNormalColor, FChangeColor : TColor ;
  83.     FSound : integer ;
  84.   protected
  85.     // overridden methods
  86.     constructor Create( AOwner: TComponent ) ; override ;
  87.     procedure Click ; override ;
  88.  
  89.     // Message Hanlders
  90.     procedure cmMouseEnter( var Msg : TMessage ) ;
  91.       message CM_MOUSEENTER ;
  92.     procedure cmMouseLeave( var Msg : TMessage ) ;
  93.       message CM_MOUSELEAVE ;
  94.   public
  95.   published
  96.     // button caption color change when mouse over button?
  97.     property EnterChange: boolean
  98.       read FEnterChange
  99.       write FEnterChange
  100.       default FALSE ;
  101.     // color to which to change
  102.     property ChangeColor: TColor
  103.       read FChangeColor
  104.       write FChangeColor ;
  105.  
  106.     // play a sound when button pressed?
  107.     property PlaySound: boolean
  108.       read FPlaySound
  109.       write FPlaySound
  110.       default FALSE ;
  111.     // Sound made if Playsound = TRUE
  112.     // use MessageBeep() constants for different sounds
  113.     property Sound: integer
  114.        read FSound
  115.        write FSound
  116.        default $FFFFFFFF ;  // speaker beep
  117.   end ;
  118.  
  119.   TListBox = class( StdCtrls.TListBox )
  120.   // Interposed TListBox:
  121.   // Adds "Smart" Horizontal scrollbar
  122.   private
  123.   protected
  124.     // overridden methods
  125.     procedure CreateParams( var Params : TCreateParams ) ; override;
  126.  
  127.     // Message Handlers
  128.     procedure LBAddString( var Msg : TMessage ) ;
  129.       message LB_ADDSTRING ;
  130.     procedure LBInsertString( var Msg : TMessage ) ;
  131.       message LB_INSERTSTRING ;
  132.     procedure LBDeleteString( var Msg : TMessage ) ;
  133.       message LB_DELETESTRING ;
  134.     procedure LBResetContent( var Msg : TMessage ) ;
  135.       message LB_RESETCONTENT ;
  136.     procedure CMFontChanged( var Msg : TMessage ) ;
  137.       message CM_FONTCHANGED ;
  138.  
  139.     // property get/set methods
  140.     procedure SetScrollWidth( Value : integer ) ;
  141.     function GetScrollWidth : integer ;
  142.  
  143.     // Auxiliary Routines
  144.     function WidthOfString( const S : string ) : integer ;
  145.     procedure AllWidths ;
  146.     procedure NewWidth( P : PChar ) ;
  147.  
  148.   public
  149.   published
  150.     // width of longest line (requires horizontal scrollbar?)
  151.     property ScrollWidth : integer
  152.       read GetScrollWidth
  153.       write SetScrollWidth ;
  154.   end ;
  155.  
  156.   FilterChars = set of char ;
  157.  
  158.   TEdit = class( StdCtrls.TEdit )
  159.   // Interposed TEdit:
  160.   // Adds Left and center justification
  161.   // Adds character filtering with optional complaint beep
  162.   private
  163.     FFilterProc : TNotifyEvent ;
  164.     FFilterChars : FilterChars ;
  165.     FFilterStr   : string ;
  166.     FErrBeep : boolean ;
  167.     FSound : integer ;
  168.     FAlignment : TAlignment ;
  169.   protected
  170.     // overridden methods
  171.     procedure CreateParams( var Params : TCreateParams ) ; override;
  172.     procedure Change ; override ;
  173.     procedure KeyPress( var Key : char ) ; override ;
  174.     procedure KeyDown( var Key : word ; Shift : TShiftState ) ; override ;
  175.  
  176.     // custom handler placeholder
  177.     procedure FilterProc ;
  178.  
  179.     // property get/set methods
  180.     procedure SetAlignment( Value : TAlignment ) ;
  181.     procedure SetFilterChars( Value : string ) ;
  182.   public
  183.   published
  184.     // Left, Center, or Right justify text
  185.     // Same constants as used in TMemo and TLabel
  186.     property Alignment: TAlignment
  187.       read FAlignment
  188.       write SetAlignment
  189.       default taLeftJustify ;
  190.     // permissable characters in edit box
  191.     // property automatically adds #8 (BackSpace)
  192.     property FilterChars : string
  193.       read FFilterStr
  194.       write SetFilterChars ;
  195.     // beep on error?
  196.     property ErrBeep: boolean
  197.       read FErrBeep
  198.       write FErrBeep
  199.       default FALSE ;
  200.     // Sound made if ErrBeep = TRUE
  201.     // use MessageBeep() constants for different sounds
  202.     property Sound: integer
  203.       read FSound
  204.       write FSound
  205.       default $FFFFFFFF ;  // speaker beep
  206.     // Custom filter function
  207.     property OnFilter : TNotifyEvent
  208.       read FFilterProc
  209.       write FFilterProc ;
  210.   end ;
  211.  
  212.   TMemo = class( StdCtrls.TMemo )
  213.   // Interposed TMemo:
  214.   // Adds caret position properties
  215.   // Adds single step Undo
  216.   private
  217.     FOnPosChange : TNotifyEvent ;
  218.   protected
  219.     // overridden methods
  220.     procedure MouseUp( Button: TMouseButton ; Shift: TShiftState ; X, Y : integer ) ; override ;
  221.     procedure KeyUp( var Key : word ; Shift : TShiftState ) ; override ;
  222.  
  223.     // custom handler placeholder
  224.     procedure PosChange ;
  225.  
  226.     // Property Get/set methods
  227.     function GetRow : longint ;
  228.     procedure SetRow( Value : longint ) ;
  229.     function GetCol : longint ;
  230.     procedure SetCol( Value : longint ) ;
  231.   public
  232.     // is last action undo-able?
  233.     function CanUndo : boolean ;
  234.     // Perform the Undo
  235.     procedure Undo ;
  236.  
  237.   published
  238.     // Line of Memo, zero based
  239.     property Row : longint
  240.       read GetRow
  241.       write SetRow
  242.       default 0 ;
  243.     // Row of Memo, zero based
  244.     property Col : longint
  245.       read GetCol
  246.       write SetCol
  247.       default 0 ;
  248.  
  249.     // Custom handler for position change
  250.     property OnPosChange : TNotifyEvent
  251.       read FOnPosChange
  252.       write FOnPosChange ;
  253.   end ;
  254.  
  255. (*****) implementation (************************************)
  256. //
  257. // Interposed TPanel's Methods
  258. //
  259. procedure TPanel.HiThere ;
  260. begin
  261.   MessageBox( 0, 'Hi There!', 'Hello Message', MB_OK or MB_ICONEXCLAMATION ) ;
  262. end;
  263.  
  264. //
  265. // Interposed TBitBtn's Methods
  266. //
  267. constructor TBitBtn.Create( AOwner: TComponent ) ;
  268. begin
  269.   inherited Create( AOwner ) ;
  270.   FNormalColor := Font.Color ;
  271.   FSound := $FFFFFFFF ;  { computer speaker beep }
  272.   FEnterChange := FALSE ;
  273.   FPlaySound := FALSE ;
  274. end;
  275.  
  276. procedure TBitBtn.Click ;
  277. begin
  278.   if FPlaySound then
  279.     MessageBeep( FSound ) ;
  280.   inherited Click ;
  281. end;
  282.  
  283. procedure TBitBtn.cmMouseEnter( var Msg : TMessage ) ;
  284. begin
  285.   inherited ;
  286.   if EnterChange then  // if want color change
  287.   begin
  288.     Font.Color := FChangeColor ;  // set to change color
  289.   end;
  290. end;
  291.  
  292. procedure TBitBtn.cmMouseLeave( var Msg : TMessage ) ;
  293. begin
  294.   if EnterChange then  // if color change enabled
  295.   begin
  296.     Font.Color := FNormalColor ;  // set back to normal color
  297.   end;
  298.   inherited ;
  299. end;
  300.  
  301. //
  302. // Interposed TListBox's Methods
  303. //
  304. procedure TListBox.CreateParams( var Params : TCreateParams ) ;
  305. begin
  306.   inherited CreateParams( Params ) ;
  307.   Params.Style := Params.Style or WS_HSCROLL ;
  308. end;
  309.  
  310. procedure TListBox.LBAddString( var Msg : TMessage ) ;
  311. begin
  312.   inherited ;
  313.   NewWidth( PChar( Msg.LParam )) ;
  314. end;
  315.  
  316. procedure TListBox.LBInsertString( var Msg : TMessage ) ;
  317. begin
  318.   inherited ;
  319.   NewWidth( PChar( Msg.LParam )) ;
  320. end;
  321.  
  322. procedure TListBox.LBDeleteString( var Msg : TMessage ) ;
  323. begin
  324.   inherited ;
  325.   AllWidths;
  326. end;
  327.  
  328. procedure TListBox.LBResetContent( var Msg : TMessage ) ;
  329. begin
  330.   inherited ;
  331.   ScrollWidth := 0 ;
  332. end;
  333.  
  334. procedure TListBox.CMFontChanged( var Msg : TMessage ) ;
  335. begin
  336.   inherited ;
  337.   AllWidths;
  338. end;
  339.  
  340. procedure TListBox.SetScrollWidth( Value : integer ) ;
  341. begin
  342.   Perform( LB_SETHORIZONTALEXTENT, Value, 0 ) ;
  343. end;
  344.  
  345. function TListBox.GetScrollWidth : integer ;
  346. begin
  347.   Result := Perform( LB_GETHORIZONTALEXTENT, 0, 0 ) ;
  348. end;
  349.  
  350. function TListBox.WidthOfString( const S : string ) : integer ;
  351. begin
  352.   Canvas.Font := Font ;
  353.   Result := Canvas.TextWidth( S + 'X' ) ;
  354. end;
  355.  
  356. procedure TListBox.AllWidths ;
  357. var
  358.   j, NewWid, Wid : integer ;
  359. begin
  360.   NewWid := 0 ;
  361.   for j := 0 to Items.Count - 1 do
  362.   begin
  363.     Wid := WidthOfString( Items[j] ) ;
  364.     if Wid > NewWid then
  365.       NewWid := Wid ;
  366.   end;
  367.   ScrollWidth := NewWid ;
  368. end;
  369.  
  370. procedure TListBox.NewWidth( P : PChar ) ;
  371. var
  372.   Wid : integer ;
  373. begin
  374.   Canvas.Font := Font ;
  375.   Wid := WidthOfString( StrPas( P )) ;
  376.   if Wid > ScrollWidth then
  377.     ScrollWidth := Wid ;
  378. end;
  379.  
  380. //
  381. // Interposed TEdit's Methods
  382. //
  383. procedure TEdit.CreateParams ( var Params : TCreateParams ) ;
  384. begin
  385.   inherited CreateParams( Params ) ;
  386.   case FAlignment of
  387.     taLeftJustify  : // Left Justification
  388.       Params.Style := Params.Style or ES_MULTILINE or ES_LEFT ;
  389.     taCenter       : // Centered
  390.       Params.Style := Params.Style or ES_MULTILINE or ES_CENTER ;
  391.     taRightJustify : // Right Justification
  392.       Params.Style := Params.Style or ES_MULTILINE or ES_RIGHT ;
  393.   end;
  394. end;
  395.  
  396. procedure TEdit.Change ;
  397. var
  398.   Caret : integer ;
  399.   SavText : string ;
  400. begin
  401.   FilterProc ;
  402.  
  403.   Caret := SelStart ;
  404.   SavText := Text ;
  405.   // Handle pasting multiple lines into control,
  406.   // shows only first line up to first #13 (Carriage Return)
  407.   // which is normal TEdit behavior
  408.   if Pos( #13, SavText ) > 0 then
  409.     SavText := Copy( SavText, 1, Pos( #13, SavText ) - 1 ) ;
  410.  
  411.   Text := SavText ;
  412.   SelStart := Caret ;
  413.  
  414.   inherited Change ;
  415. end;
  416.  
  417. procedure TEdit.FilterProc ;
  418. begin
  419.   if Assigned( FFilterProc ) then
  420.     FFilterProc( Self ) ;
  421. end;
  422.  
  423. procedure TEdit.KeyPress( var Key : char ) ;
  424. begin
  425.   if not ( FFilterStr = '' ) then
  426.   begin
  427.     // prevent return or enter keys from adding lines
  428.     if ( Key = #10 ) or (Key = #13 )then
  429.       Key := #0 ;
  430.  
  431.     // process filter chars & add BackSpace (#8)
  432.     if not ( Key in ( FFilterChars + [#8] )) then
  433.     begin
  434.       if ErrBeep then
  435.         MessageBeep( FSound ) ;
  436.       Key := #0 ;
  437.     end ;
  438.   end;
  439.   inherited KeyPress( Key ) ;
  440. end;
  441.  
  442. procedure TEdit.KeyDown( var Key : word ; Shift : TShiftState ) ;
  443. begin
  444.   // prevent Ctrl-Enter or Ctrl-Tab from adding lines
  445.   if ((Key = VK_RETURN) or (Key = VK_TAB)) and (ssCtrl in Shift) then
  446.     Key := 0 ;
  447.   inherited KeyDown( Key, Shift ) ;
  448. end;
  449.  
  450. procedure TEdit.SetAlignment( Value : TAlignment ) ;
  451. begin
  452.   if FAlignment <> Value then
  453.   begin
  454.     FAlignment := Value;
  455.     RecreateWnd;  // inherited from TWinControl
  456.       // rebuilds Window based on current styles
  457.   end;
  458. end;
  459.  
  460. procedure TEdit.SetFilterChars( Value : string ) ;
  461. var
  462.   j : longint ;
  463. begin
  464.   if FFilterStr <> Value then
  465.   begin
  466.     FFilterStr := Value ;
  467.     FFilterChars := [] ;
  468.     for j := 1 to Length( FFilterStr ) do
  469.     begin
  470.       FFilterChars := FFilterChars + [FFilterStr[j]] ;
  471.     end ;
  472.   end;
  473. end;
  474.  
  475. //
  476. // Interposed TMemo's Methods
  477. //
  478. procedure TMemo.PosChange ;
  479. begin
  480.   if Assigned( FOnPosChange ) then
  481.     FOnPosChange( Self ) ;
  482. end;
  483.  
  484. function TMemo.GetRow : longint ;
  485. begin
  486.   // get line #
  487.   Result := Perform( EM_LINEFROMCHAR, $FFFF, 0 ) ;
  488. end;
  489.  
  490. procedure TMemo.SetRow( Value : longint ) ;
  491. var
  492.   VCol : longint ;
  493. begin
  494.   VCol := GetCol ;
  495.   SelStart := Perform( EM_LINEINDEX, Value, 0 ) ;
  496.   SetCol( VCol ) ;
  497.   // no need to call PosChange, it's in SetCol
  498. end;
  499.  
  500. function TMemo.GetCol : longint ;
  501. var
  502.   ro : integer ;
  503. begin
  504.   // get line #
  505.   ro := Perform( EM_LINEFROMCHAR, $FFFF, 0 ) ;
  506.   // interpolate column position from SelStart
  507.   Result := SelStart - Perform( EM_LINEINDEX, ro, 0 ) ;
  508. end;
  509.  
  510. procedure TMemo.SetCol( Value : longint ) ;
  511. var
  512.   VCol : longint;
  513. begin
  514.   VCol := Perform( EM_LINELENGTH, Perform( EM_LINEINDEX, GetRow, 0), 0 ) ;
  515.   if VCol > Value then
  516.     VCol := Value ;
  517.   SelStart := Perform( EM_LINEINDEX, GetRow, 0 ) + VCol ;
  518.   PosChange ;
  519. end;
  520.  
  521. procedure TMemo.MouseUp( Button: TMouseButton ; Shift: TShiftState ; X, Y : integer ) ;
  522. begin
  523.   inherited MouseUp( Button, Shift, X, Y ) ;
  524.   PosChange ;
  525. end;
  526.  
  527. procedure TMemo.KeyUp( var Key : word ; Shift : TShiftState ) ;
  528. begin
  529.   inherited KeyUp( Key, Shift ) ;
  530.   PosChange ;
  531. end;
  532.  
  533. function TMemo.CanUndo : boolean ;
  534. begin
  535.   Result := Perform( EM_CANUNDO, 0, 0 ) <> 0  ;
  536. end;
  537.  
  538. procedure TMemo.Undo ;
  539. begin
  540.   Perform( EM_UNDO, 0, 0 )
  541. end;
  542.  
  543. (*****) initialization (************************************)
  544.   (* none *)
  545. end.
  546.